﻿#include "work_tcp.h"
#include "ui_home.h"
#include <QMessageBox>
#include <QDataStream>

#define GW_TCP_PORT 12309 // 网关tcp端口号

/**
 * @brief workTcp::workTcp 构造函数
 * @param cluster 要连接的簇控制器
 * @param parent  父对象 用于析构
 */
WorkTcp::WorkTcp(udpDeviceInfo_t cluster, QObject *parent)
{
    if (parent != 0)
    {
        this->setParent(parent);
    }

    /* 初始化工具类：xml */
    m_xml = new ToolXml;

    /* 初始化簇控制器 */
    this->m_c = new Cluster;

    this->m_cluster = cluster;

    // 登录消息处理
    connect(this, &WorkTcp::recvLoad, this, &WorkTcp::loadHandle);
    // 心跳消息处理
    connect(this, &WorkTcp::recvHeartbeat, this, &WorkTcp::hbHandle);
    // 获取簇控制器消息处理
    connect(this, &WorkTcp::recvGetCluster, this, &WorkTcp::getClusterHandle);
    // 获取就地控制器消息处理
    connect(this, &WorkTcp::recvGetLocal, this, &WorkTcp::getLocalHandle);
    // 设置电机消息处理
    connect(this, &WorkTcp::recvSetMotor, this, &WorkTcp::setMotorHandle);
    // 重启 复位 就地控制器
    connect(this, &WorkTcp::recvRebootLocal, this, &WorkTcp::rebootLocalHandle);
    // 设置就地控制器的回复
    connect(this, &WorkTcp::recvSetLocal, this, &WorkTcp::setLocalHandle);
    // 设置簇控制器的回复
    connect(this, &WorkTcp::recvSetCluster, this, &WorkTcp::setClusterHandle);
    // 设置cmd的回复
    connect(this, &WorkTcp::recvGetCmd, this, &WorkTcp::getCmdHandle);
    connect(this, &WorkTcp::recvSetCmd, this, &WorkTcp::setCmdHandle);
    // 设置dns的回复
    connect(this, &WorkTcp::recvGetDns, this, &WorkTcp::getDnaHandle);
    connect(this, &WorkTcp::recvSetDns, this, &WorkTcp::setDnsHandle);
    // 更新电机状态
    connect(this, &WorkTcp::recvMotorRefr, this, &WorkTcp::refrMotorHandle);
}

WorkTcp::~WorkTcp()
{
    delete this->m_c;
    delete this->m_xml;

    if (this->m_tcp != nullptr)
        m_tcp->deleteLater();
    if (this->m_timer != nullptr)
        m_timer->deleteLater();
}

/**
 * @brief WorkTcp::getClusterInfo 发送消息用于获取簇控制器的信息
 */
void WorkTcp::getClusterInfo()
{
    QString xml = m_xml->makeGetClusterXml(m_cluster.sn);

    m_tcp->write(this->dataPackage(xml, CMD_GET_CLUSTER));
    qDebug() << "getClusterInfo Send: " + xml;
}

void WorkTcp::setMotor(QString xml)
{
    QByteArray tBuf = this->dataPackage(xml, CMD_SET_MOTOR);

    qDebug() << "Set Motor Send: " << xml;
    this->m_tcp->write(tBuf);
}

void WorkTcp::setMotorHandle(QString xml)
{
    int ret = m_xml->readRetXml(xml);
    if (!ret)
    {
        qDebug() << "设置电机成功";
        emit msgbox("提示", "设置电机成功", 0);
    }
    else
    {
        qDebug() << "设置电机失败";
        emit msgbox("提示", "设置电机失败", 1, ret);
    }
}

void WorkTcp::rebootLocalHandle(QString xml)
{
    int ret = m_xml->readRetXml(xml);
    if (!ret)
    {
        qDebug() << "控制就地控制器成功";
        emit msgbox("提示", "控制就地控制器成功", 0);
    }
    else
    {
        qDebug() << "控制就地控制器失败";
        emit msgbox("提示", "控制就地控制器失败", 1, ret);
    }
}

void WorkTcp::refrMotorHandle(QString xml)
{
    qDebug() << "开始处理 更新电机状态的回复";
    LocalDev * local = new LocalDev;
    int ret = this->m_xml->readMotorRefrXml(xml, local);
    qDebug() << QString("ret: %1").arg(ret);
    if (ret != 0)
    {
        qDebug() << QString("读取更新电机状态信息失败！ret: %1").arg(ret);
    }
    else
    {
        qDebug() << "读取更新电机状态信息成功！";
        emit motorRefrSuccess(local);
    };
}

/**
 * @brief workTcp::connectToCluster 槽函数：连接主界面信号 连接簇控制器
 */
void WorkTcp::connectToCluster()
{
    if (this->m_tcp != nullptr)
    {
        qDebug() << "关闭TCP套接字！";
        this->m_tcp->disconnect();
        this->m_tcp->close();
        this->m_tcp->deleteLater();
        this->m_tcp = nullptr;
    }

    qDebug() << m_cluster.sn << m_cluster.ip << "连接中...";

    // TCP初始化
    this->m_tcp = new QTcpSocket(this);

    // 信号槽：TCP连接成功
    connect(m_tcp, &QTcpSocket::connected, this, &WorkTcp::sendLoadToCluster);

    // 信号槽：TCP断开连接
    connect(m_tcp, &QTcpSocket::disconnected, this, [=](){
        m_tcp->close();
        m_tcp->deleteLater();
        m_tcp = nullptr;

        emit clusterDisconnected();
    });

    // 信号槽：TCP接收数据
    connect(m_tcp, &QTcpSocket::readyRead, this, &WorkTcp::tcpRecvData);

    // 连接簇控制器
//    m_tcp->connectToHost(QHostAddress("192.168.1.193"), GW_TCP_PORT);
    m_tcp->connectToHost(QHostAddress(m_cluster.ip), GW_TCP_PORT);
}


/**
 * @brief workTcp::connectToClusterSuccess 槽函数：成功连接到簇控制器
 * @note 向簇控制器发送登录请求
 */
void WorkTcp::sendLoadToCluster()
{
    /* 发送簇控制器成功连接信号 */
    emit clusterConnected(m_cluster.sn);

    /* 创建登录xml */
    QString xml = m_xml->makeLoadXml(m_cluster.sn);

    /* 发送数据 */
    m_tcp->write(this->dataPackage(xml, CMD_LOAD));
    qDebug() << "Send: " + xml;
}

/**
 * @brief WorkTcp::tcpRecvData 槽函数：接收到tcp的数据
 */
void WorkTcp::tcpRecvData()
{
    QByteArray rBuf = m_tcp->readAll();
    quint8 cmd = dataUnpack(rBuf);
    qDebug() << QString("Read[%1]: %2").arg(cmd).arg(this->m_xBuf);

    switch (cmd) {
    case CMD_LOAD:
        emit recvLoad(this->m_xBuf);
        break;
    case CMD_HB:
        emit recvHeartbeat(this->m_xBuf);
        break;
    case CMD_GET_CLUSTER:
        emit recvGetCluster(this->m_xBuf);
        break;
    case CMD_GET_LOCAL:
        emit recvGetLocal(this->m_xBuf);
        break;
    case CMD_SET_MOTOR:
        emit recvSetMotor(this->m_xBuf);
        break;
    case CMD_REBOOT_LOCAL:
        emit recvRebootLocal(this->m_xBuf);
        break;
    case CMD_SET_LOCAL:
        emit recvSetLocal(this->m_xBuf);
        break;
    case CMD_SET_CLUSTER:
        emit recvSetCluster(this->m_xBuf);
        break;
    case CMD_SET_CMD:
        emit recvSetCmd(this->m_xBuf);
        break;
    case CMD_SET_DNS:
        emit recvSetDns(this->m_xBuf);
        break;
    case CMD_GET_CMD:
        emit recvGetCmd(this->m_xBuf);
        break;
    case CMD_GET_DNS:
        emit recvGetDns(this->m_xBuf);
        break;
    case CMD_MOTOR_REFR:
        emit recvMotorRefr(this->m_xBuf);
        break;

    default:
        qDebug() << "未能识别的命令类型！";
        break;
    }
}

void WorkTcp::sendHeartbeat()
{
    this->m_timer->setInterval(120 * 1000);

    /* 创建心跳xml */
    QString xml = m_xml->makeHeartbeatXml(m_cluster.sn);

    /* 发送数据 */
    m_tcp->write(this->dataPackage(xml, CMD_HB));
    qDebug() << "心跳发送: " + xml;
}

void WorkTcp::sendGetLocal(int ldIndex)
{
    this->m_ldIndex = ldIndex;

    /* 创建xml */
    QString xml = m_xml->makeGetLocalXml(m_cluster.sn, m_c->m_localDev.at(m_ldIndex).busID(), m_c->m_localDev.at(m_ldIndex).getMAddr());

    /* 发送数据 */
    m_tcp->write(this->dataPackage(xml, CMD_GET_LOCAL));
    qDebug() << "获取就地控制器: " + xml;
}

void WorkTcp::sendClusterReboot(QString sn, int busId, QString maddr, int cmdType)
{
    /* 创建xml */
    QString xml = m_xml->makeRebootLocal(sn, busId, maddr, (ClusterSet_e)cmdType);

    /* 发送数据 */
    m_tcp->write(this->dataPackage(xml, CMD_REBOOT_LOCAL));
    qDebug() << "重启/重置 就地控制器: " + xml;
}

void WorkTcp::sendSetLocal(QString xml)
{
    m_tcp->write((this->dataPackage(xml, CMD_SET_LOCAL)));
    qDebug() << "Set Local Device Send: " << xml;
}

void WorkTcp::sendSetCluster(QString xml)
{
    m_tcp->write((this->dataPackage(xml, CMD_SET_CLUSTER)));
    qDebug() << "Set Cluster Device Send: " << xml;
}

void WorkTcp::sendSetCmd(QString xml)
{
    m_tcp->write((this->dataPackage(xml, CMD_SET_CMD)));
    qDebug() << "Set Cmd Device Send: " << xml;
}

void WorkTcp::sendSetDna(QString xml)
{
    m_tcp->write((this->dataPackage(xml, CMD_SET_DNS)));
    qDebug() << "Set Dns Device Send: " << xml;
}

void WorkTcp::sendGetCmd(QString xml)
{
    m_tcp->write((this->dataPackage(xml, CMD_GET_CMD)));
    qDebug() << "Get Cmd Device Send: " << xml;
}

void WorkTcp::sendGetDna(QString xml)
{
    m_tcp->write((this->dataPackage(xml, CMD_GET_DNS)));
    qDebug() << "Get Dns Device Send: " << xml;
}

void WorkTcp::loadHandle(QString xml)
{
    qDebug() << "开始处理网关返回的登录消息";
    int ret = this->m_xml->readLoadXml(xml, this->m_clusterInfo);
    qDebug() << QString("ret: %1").arg(ret);
    if (ret != 0)
    {
        qDebug() << QString("xml错误！ret: %1").arg(ret);
        emit sendRetError(ret);
        QTimer::singleShot(3000, this, [=](){
            /* 创建登录xml */
            QString xml = m_xml->makeLoadXml(m_cluster.sn);

            /* 发送数据 */
            m_tcp->write(this->dataPackage(xml, CMD_LOAD));
            qDebug() << "Send: " + xml;
        });
    }
    else
    {
        if (this->m_timer != nullptr)
        {
            this->m_timer->deleteLater();
        }
        this->m_timer = new QTimer(this);
        this->m_timer->setInterval(1);

        connect(this->m_timer, &QTimer::timeout, this, &WorkTcp::sendHeartbeat);

        this->m_timer->start();
    }
}

void WorkTcp::hbHandle(QString xml)
{
    qDebug() << "开始处理网关返回的心跳消息";
    int ret = this->m_xml->readHeartbeatXml(xml);
    qDebug() << QString("ret: %1").arg(ret);
    if (ret != 0)
    {
        qDebug() << QString("与设备心跳异常！ret: %1").arg(ret);
        emit sendRetError(ret);
        this->m_timer->setInterval(3000);
        this->m_tcpEnable = false;
    }
    else
    {
        qDebug() << "心跳上报成功！";
        if (!this->m_tcpEnable)
        {
            this->m_tcpEnable = true;
            this->getClusterInfo();
        }
    }
}

void WorkTcp::getClusterHandle(QString xml)
{
    qDebug() << "开始处理 簇控制器信息";
    int ret = this->m_xml->readGetClusterXml(xml, this->m_c);
    qDebug() << QString("ret: %1").arg(ret);
    if (ret != 0)
    {
        qDebug() << QString("获取簇控制器信息失败！ret: %1").arg(ret);
        emit sendRetError(ret);
    }
    else
    {
        qDebug() << "获取簇控制器信息成功！";
        qDebug() << m_c->ip() << "cnt: " << m_c->localDevCnt();
        qDebug() << m_c->ip() << "localDev.size: " << m_c->m_localDev.size();
        emit getClusterSuccess(this->m_c);
    }
}

void WorkTcp::getLocalHandle(QString xml)
{
    qDebug() << "开始处理 就地控制器信息";
    int ret = this->m_xml->readGetLocalXml(xml, (LocalDev *)&this->m_c->m_localDev.at(m_ldIndex));
    qDebug() << QString("ret: %1").arg(ret);
    if (ret != 0)
    {
        qDebug() << QString("获取就地控制器信息失败！ret: %1").arg(ret);
        emit sendRetError(ret);
    }
    else
    {
        qDebug() << "获取就地控制器信息成功！";
        qDebug() << "Motor.size: " << m_c->m_localDev.at(m_ldIndex).m_motor.size();
        emit getLocalSuccess((LocalDev *)&this->m_c->m_localDev.at(m_ldIndex));
    }
}

void WorkTcp::setLocalHandle(QString xml)
{
    qDebug() << "开始处理 设置就地控制器的回复";
    int ret = this->m_xml->readRetXml(xml);
    qDebug() << QString("ret: %1").arg(ret);
    if (ret != 0)
    {
        qDebug() << QString("设置就地控制器信息失败！ret: %1").arg(ret);
        emit msgbox("操作失败", "设置就地控制器信息失败！", 1, ret);
    }
    else
    {
        qDebug() << "设置就地控制器信息成功！";
        emit msgbox("操作成功", "设置就地控制器信息成功！", 0);
    }
}

void WorkTcp::setCmdHandle(QString xml)
{
    qDebug() << "开始处理 设置CMD的回复";
    int ret = this->m_xml->readRetXml(xml);
    qDebug() << QString("ret: %1").arg(ret);
    if (ret != 0)
    {
        qDebug() << QString("设置CMD信息失败！ret: %1").arg(ret);
        emit msgbox("操作失败", "设置CMD信息失败！", 1, ret);
    }
    else
    {
        qDebug() << "设置CMD信息成功！";
        emit msgbox("操作成功", "设置CMD信息成功！", 0);
    }
}

void WorkTcp::setDnsHandle(QString xml)
{
    qDebug() << "开始处理 设置DNA的回复";
    int ret = this->m_xml->readRetXml(xml);
    qDebug() << QString("ret: %1").arg(ret);
    if (ret != 0)
    {
        qDebug() << QString("设置DNA信息失败！ret: %1").arg(ret);
        emit msgbox("操作失败", "设置DNA信息失败！", 1, ret);
    }
    else
    {
        qDebug() << "设置DNA信息成功！";
        emit msgbox("操作成功", "设置DNA信息成功！", 0);
    }
}

void WorkTcp::getCmdHandle(QString xml)
{
    qDebug() << "开始处理 读取CMD的回复";
    LocalDev * local = new LocalDev;
    localCmd_t cmd;
    int ret = this->m_xml->readGetCmdXml(xml, &cmd);
    qDebug() << QString("ret: %1").arg(ret);
    if (ret != 0)
    {
        qDebug() << QString("读取CMD信息失败！ret: %1").arg(ret);
        emit msgbox("操作失败", "读取CMD信息失败！", 1, ret);
    }
    else
    {
        qDebug() << "读取CMD信息成功！";
        local->setCmd(cmd);
        emit getCmdSuccess(local);
    };
}

void WorkTcp::getDnaHandle(QString xml)
{
    qDebug() << "开始处理 读取DNA的回复";
    LocalDev * local = new LocalDev;
    localDna_t dna;
    int ret = this->m_xml->readGetDnaXml(xml, &dna);
    qDebug() << QString("ret: %1").arg(ret);
    if (ret != 0)
    {
        qDebug() << QString("读取DNA信息失败！ret: %1").arg(ret);
        emit msgbox("操作失败", "读取DNA信息失败！", 1, ret);
    }
    else
    {
        qDebug() << "读取DNA信息成功！";
        local->setDna(dna);
        emit getDnaSuccess(local);
    }
}

void WorkTcp::setClusterHandle(QString xml)
{
    qDebug() << "开始处理 设置簇控制器的回复";
    int ret = this->m_xml->readRetXml(xml);
    qDebug() << QString("ret: %1").arg(ret);
    if (ret != 0)
    {
        qDebug() << QString("设置簇控制器信息失败！ret: %1").arg(ret);
        emit msgbox("操作失败", "设置簇控制器信息失败！", 1, ret);
    }
    else
    {
        qDebug() << "设置簇控制器信息成功！";
        emit msgbox("操作成功", "设置簇控制器信息成功！", 0);
    }
}

/**
 * @brief WorkTcp::dataPackage xml打包
 * @param xml
 * @return 打包后的数据
 */
QByteArray WorkTcp::dataPackage(QString xml, quint8 cmd)
{
    QByteArray byteXml = xml.toUtf8();
    int dataLen = byteXml.size();

    QByteArray data;

    int cnt = 0;
    char cdata[16] = {0};
    cdata[cnt++] = 0xfa; // 包头
    cdata[cnt++] = 0xf5;
    cdata[cnt++] = 0xf6;
    cdata[cnt++] = (m_serial >> 8);   // 流水号
    cdata[cnt++] = m_serial++;
    cdata[cnt++] = cmd; // 命令类型
    cdata[cnt++] = 0x00; // 加密
    cdata[cnt++] = 0x00; // 保留
    cdata[cnt++] = (dataLen >> 24); // 数据长度
    cdata[cnt++] = (dataLen >> 16);
    cdata[cnt++] = (dataLen >> 8);
    cdata[cnt++] = dataLen;

    data.append(cdata, 12);
    data.append(byteXml);

    // 包尾
    data.append(0xFA);
    data.append(0xF6);
    data.append(0xF5);

    return data;
}

/**
 * @brief WorkTcp::dataUnpack xml解包
 * @param package 接收到的数据包
 * @return 接收到的命令类型 -1为解析失败
 */
int WorkTcp::dataUnpack(QByteArray package)
{
    QByteArray head = package.left(12);

    /* 校验帧头 */
    if (head[0] != (char)0xfa && head[1] != (char)0xf5 && head [2] != (char)0xf6)
    {
        qDebug() << "包头不匹配！";
        return -1;
    }

    /* 校验数据长度 */
    int len = 0;
    QDataStream dataStream(head.right(4));
    dataStream.setByteOrder(QDataStream::BigEndian);
    dataStream >> len;
    if (len != package.size() - 15)
    {
        qDebug() << "数据长度不匹配！";
        return -1;
    }

    this->m_xBuf = QString::fromUtf8(package.mid(12, len));
//    qDebug() << "cmd: " << static_cast<uint8_t>(head[5]);
    return static_cast<int>(head[5]);
}

Cluster *WorkTcp::getC() const
{
    return m_c;
}







